package com.autumn.util.reflect;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

import com.autumn.util.EqualsUtils;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.StringUtils;
import com.autumn.util.TypeUtils;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import com.esotericsoftware.reflectasm.FieldAccess;
import com.esotericsoftware.reflectasm.MethodAccess;

/**
 * 反射实用工具
 * 
 * @author 杨昌国
 *
 *         2017-09-28 14:41:03
 */
public class ReflectUtils {

	private final static Map<Class<?>, MethodAccess> METHOD_ACCESS_MAP = new ConcurrentHashMap<>();
	private final static Map<Class<?>, FieldAccess> FIELD_ACCESS_MAP = new ConcurrentHashMap<>();
	private final static Map<Class<?>, ConstructorAccess<?>> CONSTRUCTOR_ACCESS_MAP = new ConcurrentHashMap<>();
	private final static Map<Class<?>, Object> ENUM_TYPE_ARRAY_MAP = new ConcurrentHashMap<>();
	private final static Map<Class<?>, Map<String, BeanProperty>> BEAN_PROPERTY_MAP = new ConcurrentHashMap<>();

	/**
	 * 获取bean属性集合（先从缓存读取，若为Null,则再执行查找，并做缓存处理）
	 * 
	 * @param beanClass
	 * @return
	 */
	public static Map<String, BeanProperty> getBeanPropertyMap(Class<?> beanClass) {
		Map<String, BeanProperty> map = BEAN_PROPERTY_MAP.get(beanClass);
		if (map == null) {
			map = findBeanPropertyMap(beanClass);
			map = Collections.unmodifiableMap(map);
			BEAN_PROPERTY_MAP.put(beanClass, map);
		}
		return map;
	}

	/**
	 * 获取枚举类型数组
	 * 
	 * @param enumType 枚举类型
	 * @return
	 */
	public static Object getEnumTypeArray(Class<?> enumType) {
		ExceptionUtils.checkNotNull(enumType, "enumType");
		Object values = ENUM_TYPE_ARRAY_MAP.get(enumType);
		if (values == null) {
			if (!Enum.class.isAssignableFrom(enumType)) {
				ExceptionUtils.throwSystemException("类型[" + enumType.getName() + "]不是 Enum 类型。");
			}
			try {
				Method method = enumType.getMethod("values");
				values = method.invoke(null);
			} catch (Exception e) {
				throw ExceptionUtils.throwSystemException(e.getMessage(), e);
			}
			ENUM_TYPE_ARRAY_MAP.put(enumType, values);
		}
		return values;
	}

	/**
	 * 查找 Bean 的属性集合
	 * 
	 * @param beanClass
	 * @return
	 * @author 杨昌国 2017-10-10 09:53:09
	 */
	public static List<BeanProperty> findBeanPropertys(Class<?> beanClass) {
		ExceptionUtils.checkNotNull(beanClass, "type");
		List<BeanProperty> propetys = new ArrayList<>();
		Set<String> propetySet = new HashSet<>();
		List<Field> fields = findFields(beanClass);
		List<Method> methods = findMethods(beanClass);
		List<PropertyDescriptor> propertyDescriptors = findPropertyDescriptors(methods);
		for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			if (propetySet.add(propertyDescriptor.getName())) {
				Field propertyField = findField(fields, propertyDescriptor.getName(),
						propertyDescriptor.getPropertyType());
				if (propertyField == null && isBooleanProperty(propertyDescriptor.getReadMethod())) {
					propertyField = findField(fields,
							PROPERTY_IS_NAME + StringUtils.upperCaseCapitalize(propertyDescriptor.getName()),
							propertyDescriptor.getPropertyType());
				}
				propetys.add(new BeanProperty(beanClass, propertyField, propertyDescriptor));
			}
		}
		propetySet.clear();
		return propetys;
	}

	/**
	 * 查找 Bean 的属性 Map
	 * 
	 * @param beanClass Bean 类型
	 * @return
	 */
	public static Map<String, BeanProperty> findBeanPropertyMap(Class<?> beanClass) {
		List<BeanProperty> propertys = findBeanPropertys(beanClass);
		Map<String, BeanProperty> map = new HashMap<String, BeanProperty>(16);
		for (BeanProperty beanProperty : propertys) {
			map.put(beanProperty.getName(), beanProperty);
		}
		propertys.clear();
		return map;
	}

	private final static String PROPERTY_GET_NAME = "get";

	private final static String PROPERTY_IS_NAME = "is";

	private final static String PROPERTY_SET_NAME = "set";

	private static boolean isBooleanProperty(Method getMethod) {
		if (getMethod == null) {
			return false;
		}
		if (getMethod.getName().length() <= PROPERTY_IS_NAME.length()) {
			return false;
		}
		if (!getMethod.getName().startsWith(PROPERTY_IS_NAME)) {
			return false;
		}
		return getMethod.getReturnType().equals(Boolean.class) || getMethod.getReturnType().equals(boolean.class);
	}

	/**
	 * 查找 PropertyDescriptor 集合，以 get 函数为基准
	 * 
	 * @param methods 方法集合
	 * @return
	 */
	private static List<PropertyDescriptor> findPropertyDescriptors(List<Method> methods) {
		List<PropertyDescriptor> methodList = new ArrayList<>();
		for (Method method : methods) {
			Class<?> returnType = method.getReturnType();
			if (!returnType.equals(void.class) && method.getParameterCount() == 0) {
				String name = null;
				if ((method.getName().length() > PROPERTY_GET_NAME.length()
						&& method.getName().startsWith(PROPERTY_GET_NAME))) {
					name = method.getName().substring(PROPERTY_GET_NAME.length());
				} else if (isBooleanProperty(method)) {
					name = method.getName().substring(PROPERTY_IS_NAME.length());
				}
				if (name != null) {
					Method setter = findMethod(methods, PROPERTY_SET_NAME + name, void.class, returnType);
					try {
						PropertyDescriptor pro = new PropertyDescriptor(StringUtils.lowerCaseCapitalize(name), method,
								setter);
						methodList.add(pro);
					} catch (IntrospectionException e) {
						return null;
					}
				}
			}
		}
		return methodList;
	}

	/**
	 * 查找所有非静态的字段(包括基类)
	 * 
	 * @param beanClass 类型
	 * @param predicate 条件
	 * @return 返回本类以及继承链上的字段集合
	 */
	public static List<Field> findFields(Class<?> beanClass, Predicate<Field> predicate) {
		List<Field> fieldList = new ArrayList<>();
		Field[] fields = beanClass.getDeclaredFields();
		for (Field field : fields) {
			int modifiers = field.getModifiers();
			if (!Modifier.isStatic(modifiers)) {
				if (predicate != null) {
					if (predicate.test(field)) {
						fieldList.add(field);
					}
				} else {
					fieldList.add(field);
				}
			}
		}
		Class<?> superclass = beanClass.getSuperclass();
		if (superclass != null && !superclass.equals(Object.class)) {
			fieldList.addAll(findFields(superclass, predicate));
		}
		return fieldList;
	}

	/**
	 * 查找所有非静态的字段(包括基类)
	 * 
	 * @param beanClass 类型
	 * @return 返回本类以及继承链上的方法集合，并按照父级向下顺序返回
	 */
	public static List<Field> findFields(Class<?> beanClass) {
		return findFields(beanClass, null);
	}

	/**
	 * 查找属性的 Set 函数
	 * 
	 * @param beanClass
	 * @return 属性名为 key
	 */
	public static Map<String, Method> findPropertiesSetMethodMap(Class<?> beanClass) {
		List<Method> methods = findMethods(beanClass);
		Map<String, Method> methodMap = new HashMap<>(16);
		for (Method method : methods) {
			if (method.getName().length() > 3 && method.getReturnType().equals(void.class)
					&& method.getParameterCount() == 1 && method.getName().startsWith(PROPERTY_SET_NAME)) {
				String name = StringUtils.lowerCaseCapitalize(method.getName().substring(3));
				methodMap.put(name, method);
			}
		}
		methods.clear();
		return methodMap;
	}

	/**
	 * 查找所有非静态的公有方法(包括基类)
	 * 
	 * @param beanClass 类型
	 * @return 返回本类以及继承链上的方法集合，并按照父级向下顺序返回
	 */
	public static List<Method> findMethods(Class<?> beanClass) {
		List<Method> methodList = new ArrayList<>();
		Class<?> superclass = beanClass.getSuperclass();
		if (superclass != null && !superclass.equals(Object.class)) {
			List<Method> superMethodList = findMethods(superclass);
			methodList.addAll(superMethodList);
		}
		Method[] methods = beanClass.getDeclaredMethods();
		for (Method method : methods) {
			int modifiers = method.getModifiers();
			if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
				Iterator<Method> iter = methodList.iterator();
				boolean isAdd = true;
				int index = 0;
				while (iter.hasNext()) {
					Method cruuentMethod = iter.next();
					if (cruuentMethod.getName().equals(method.getName())
							&& TypeUtils.equalsTypes(cruuentMethod.getParameterTypes(), method.getParameterTypes())) {
						isAdd = false;
						methodList.set(index, method);
						break;
					}
					index++;
				}
				if (isAdd) {
					methodList.add(method);
				}
			}
		}
		return methodList;
	}

	/**
	 * 查找非静态的字段
	 * 
	 * @param fields 字段集合
	 * @param name   名称
	 * @param type   类型
	 * @return
	 */
	public static Field findField(Iterable<Field> fields, String name, Class<?> type) {
		for (Field field : fields) {
			int modifiers = field.getModifiers();
			if (field.getName().equals(name) && field.getType().equals(type) && !Modifier.isStatic(modifiers)) {
				return field;
			}
		}
		return null;
	}

	/**
	 * 查找非静态的字段
	 * 
	 * @param fields 字段集合
	 * @param name   名称
	 * @param type   类型
	 * @return
	 */
	public static Field findField(Field[] fields, String name, Class<?> type) {
		for (Field field : fields) {
			int modifiers = field.getModifiers();
			if (field.getName().equals(name) && field.getType().equals(type) && !Modifier.isStatic(modifiers)) {
				return field;
			}
		}
		return null;
	}

	/**
	 * 查找非静态的公有方法
	 * 
	 * @param methods        方法集合
	 * @param name           名称
	 * @param returnType     返回类型
	 * @param parameterTypes 参数类型
	 * @return
	 */
	public static Method findMethod(Iterable<Method> methods, String name, Class<?> returnType,
			Class<?>... parameterTypes) {
		for (Method method : methods) {
			int modifiers = method.getModifiers();
			if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && method.getName().equals(name)) {
				if (method.getReturnType().equals(returnType)) {
					if (EqualsUtils.arrayValueEquals(parameterTypes, method.getParameterTypes())) {
						return method;
					}
				}
			}
		}
		return null;
	}

	/**
	 * 查找非静态的公有方法
	 * 
	 * @param methods        方法集合
	 * @param name           名称
	 * @param returnType     返回类型
	 * @param parameterTypes 参数类型
	 * @return
	 */
	public static Method findMethod(Method[] methods, String name, Class<?> returnType, Class<?>... parameterTypes) {
		for (Method method : methods) {
			int modifiers = method.getModifiers();
			if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && method.getName().equals(name)) {
				if (method.getReturnType().equals(returnType)) {
					if (EqualsUtils.arrayValueEquals(parameterTypes, method.getParameterTypes())) {
						return method;
					}
				}
			}
		}
		return null;
	}

	/**
	 * 查找非静态的公有方法
	 * 
	 * @param beanClass      Bean 类型
	 * @param name           名称
	 * @param returnType     返回类型
	 * @param parameterTypes 参数类型
	 * @return
	 */
	public static Method findMethod(Class<?> beanClass, String name, Class<?> returnType, Class<?>... parameterTypes) {
		Method[] methods = beanClass.getDeclaredMethods();
		for (Method method : methods) {
			int modifiers = method.getModifiers();
			boolean status = Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers);
			boolean exist = method.getName().equals(name) && method.getReturnType().equals(returnType)
					&& TypeUtils.equalsTypes(parameterTypes, method.getParameterTypes());
			if (status && exist) {
				return method;
			}
		}
		Class<?> superclass = beanClass.getSuperclass();
		if (superclass != null && !superclass.equals(Object.class)) {
			return findMethod(superclass, name, returnType, parameterTypes);
		}
		return null;
	}

	/**
	 * 获取方法访问
	 * 
	 * @param type
	 * @return
	 * @author 杨昌国 2017-09-28 14:43:38
	 */
	public static MethodAccess getMethodAccess(Class<?> type) {
		ExceptionUtils.checkNotNull(type, "type");
		MethodAccess access = METHOD_ACCESS_MAP.get(type);
		if (access == null) {
			access = MethodAccess.get(type);
			METHOD_ACCESS_MAP.put(type, access);
		}
		return access;
	}

	/**
	 * 获取字段访问
	 * 
	 * @param type
	 * @return
	 * @author 杨昌国 2017-09-28 14:43:38
	 */
	public static FieldAccess getFieldAccess(Class<?> type) {
		ExceptionUtils.checkNotNull(type, "type");
		FieldAccess access = FIELD_ACCESS_MAP.get(type);
		if (access == null) {
			access = FieldAccess.get(type);
			FIELD_ACCESS_MAP.put(type, access);
		}
		return access;
	}

	/**
	 * 获取构造访问
	 * 
	 * @param type
	 * @return
	 * @author 杨昌国 2017-09-28 14:43:38
	 */
	@SuppressWarnings("unchecked")
	public static <T> ConstructorAccess<T> getConstructorAccess(Class<T> type) {
		ExceptionUtils.checkNotNull(type, "type");
		ConstructorAccess<?> access = CONSTRUCTOR_ACCESS_MAP.get(type);
		if (access == null) {
			access = ConstructorAccess.get(type);
			CONSTRUCTOR_ACCESS_MAP.put(type, access);
		}
		return (ConstructorAccess<T>) access;
	}

	/**
	 * 获取泛型实际参数类型,若多继承的情况下，必须按顺序继承，增加时必须在最后一个才准确
	 * 
	 * @param currentClass 当前类
	 * @param rootClass    根类
	 * @return
	 */
	public static List<Class<?>> getGenericActualArgumentsType(Class<?> currentClass, Class<?> rootClass) {
		List<List<Class<?>>> classItems = new ArrayList<>();
		Class<?> nextClass = currentClass;
		do {
			Type type = nextClass.getGenericSuperclass();
			if (type instanceof ParameterizedType) {
				ParameterizedType parType = (ParameterizedType) type;
				Type[] types = parType.getActualTypeArguments();
				List<Class<?>> classList = new ArrayList<>();
				for (Type t : types) {
					if (t instanceof Class<?>) {
						classList.add((Class<?>) t);
					}
				}
				if (classList.size() > 0) {
					classItems.add(classList);
				}
			}
			nextClass = nextClass.getSuperclass();
		} while (!nextClass.equals(Object.class) && !nextClass.equals(rootClass));
		List<Class<?>> items = new ArrayList<>();
		for (int i = classItems.size() - 1; i >= 0; i--) {
			items.addAll(classItems.get(i));
		}
		classItems.clear();
		return items;
	}

	/**
	 * 获取泛型参实际参数类型Map(注意，若整个链条若出现参数名重复，则取直接上级)
	 * 
	 * @param beanClass
	 * @return Key = 泛型参数名称,Value 实际类型
	 */
	public static Map<String, Class<?>> getGenericActualArgumentsTypeMap(Class<?> beanClass) {
		Map<String, Class<?>> typeMap = new HashMap<>(16);
		Class<?> nextClass = beanClass;
		do {
			Type type = nextClass.getGenericSuperclass();
			setGenericActualArgumentsType(type, typeMap);
			nextClass = nextClass.getSuperclass();
		} while (!nextClass.equals(Object.class));
		Type[] genTypes = beanClass.getGenericInterfaces();
		for (Type genType : genTypes) {
			setGenericActualArgumentsType(genType, typeMap);
		}
		return typeMap;
	}

	/**
	 * 
	 * @param type
	 * @param typeMap
	 *
	 */
	private static void setGenericActualArgumentsType(Type type, Map<String, Class<?>> typeMap) {
		if (type != null && type instanceof ParameterizedType) {
			ParameterizedType parType = (ParameterizedType) type;
			if (parType.getRawType() instanceof Class<?>) {
				Class<?> rawClass = (Class<?>) parType.getRawType();
				TypeVariable<?>[] typeVars = rawClass.getTypeParameters();
				Type[] types = parType.getActualTypeArguments();
				for (int i = 0; i < types.length; i++) {
					Type t = types[i];
					if (t instanceof Class<?>) {
						TypeVariable<?> typeVar = typeVars[i];
						if (!typeMap.containsKey(typeVar.getName())) {
							typeMap.put(typeVar.getName(), (Class<?>) t);
						}
					} else {
						if (t instanceof ParameterizedType) {
							setGenericActualArgumentsType(t, typeMap);
						}
					}
				}
			}
		}
	}

}
